package ink.xiaobaibai.controller.onePay;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import ink.xiaobaibai.bo.RangeParams;
import ink.xiaobaibai.bo.SinglePayBo;
import ink.xiaobaibai.common.Common;
import ink.xiaobaibai.common.CouponStatus;
import ink.xiaobaibai.common.MySecurity;
import ink.xiaobaibai.common.WxPayment;
import ink.xiaobaibai.entity.*;
import ink.xiaobaibai.enumOxy.DeviceStatus;
import ink.xiaobaibai.multipleClick.ForbidMultipleClick;
import ink.xiaobaibai.response.ResponseFormat;
import ink.xiaobaibai.service.*;
import ink.xiaobaibai.service.monthly.IDelayTaskCommonService;
import ink.xiaobaibai.serviceOxy.IMqttService;
import ink.xiaobaibai.serviceOxy.IRedisUtils;
import ink.xiaobaibai.serviceOxy.onePay.IGeoDeviceService;
import ink.xiaobaibai.serviceOxy.pay.IPayService;
import ink.xiaobaibai.serviceOxy.pay.impl.PayServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;


/**
 * 臭氧机
 *
 * @author 小白白
 * @since 2021-05-16
 */
@Api(tags = "机器接口")
@RestController
@CrossOrigin
@RequestMapping("/ozone-device")
public class OzoneDeviceController {

    @Autowired
    private IOzoneDeviceService ozoneDeviceService;

    @Autowired
    private IGeoDeviceService geoDeviceService;

    @Autowired
    private IPayService payService;

    @Autowired
    private ICouponService couponService;

    @Autowired
    private ICouponUserService couponUserService;

    @Autowired
    private IDelayTaskCommonService delayTaskCommonService;

    @Autowired
    private IOzonePriceService priceService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private IRedisUtils redisUtils;

    @Autowired
    private IJumpService_V2 jumpService_v2;

    @Autowired
    private ICouponCodeService couponCodeService;

    @Autowired
    private IMqttService mqttService;

    /**
     * map需要传三个数据  lat，lng，distance
     */
    @ApiOperation(value = "根据该用户当前所在的gps点查询一定范围内的所有机器(距离为半径 单位:km)")
    @PostMapping("/getRangeDevice")
    public ResponseFormat getRangeDevice(@RequestBody @Validated RangeParams rangeParams) {
        return ResponseFormat.success(this.geoDeviceService.getDeviceListNearbyGeo(rangeParams.getLng(), rangeParams.getLat(), rangeParams.getDistance()));
    }

    /**
     * 根据机器码查询
     */
    @GetMapping("/item")
    @ApiOperation(value = "查询臭氧机(只有空闲状态才可以进行下一步支付)", notes = "根据机器码查询臭氧机")
    public ResponseFormat<OzoneDevice> findByCode(@RequestParam("code") String code) {
        QueryWrapper<OzoneDevice> q1 = new QueryWrapper<>();
        q1.select("id", "device_code", "address", "status", "lng", "lat", "oxygen_id", "province_city", "delayed_time", "work_stop_time");
        q1.eq("device_code", code);
        OzoneDevice ozoneDevice = this.ozoneDeviceService.getOne(q1);
        if (ozoneDevice == null) {
            return ResponseFormat.fail("当前机器不存在");
        }
        if (ozoneDevice.getStatus().equals(DeviceStatus.INUSE.getStatus()) && ozoneDevice.getWorkStopTime().getTime() < System.currentTimeMillis()) {
            ozoneDevice.setStatus(DeviceStatus.FREE.getStatus());
        }
        return ResponseFormat.success(ozoneDevice);
    }

    /**
     * 当前机器是否在线(小程序端应该异步请求此接口)
     */
    @GetMapping("/jump")
    @ApiOperation("当前机器是否在线(小程序端应该异步请求此接口)")
    public ResponseFormat jump(@RequestParam("deviceCode") String deviceCode) {
//        boolean jump = mqttService.checkOnePayDevice(deviceCode);
        // TODO 默认在线,后期改为不在线
        boolean jump = true;
        return jump ? ResponseFormat.success() : ResponseFormat.fail("当前机器不在线,请勿支付");
    }


    /**
     * 获取当前这台机器的 三段 价格和时间
     */
    @ApiOperation(value = "获取当前机器的价格和时间段(传入机器id)")
    @GetMapping("/item/price/{id}")
    public ResponseFormat findPriceListByDeviceId(@PathVariable("id") Integer deviceId) {
        QueryWrapper<OzoneDevice> q0 = new QueryWrapper<>();
        q0.select("price_id");
        q0.eq("id", deviceId);
        Integer priceId = this.ozoneDeviceService.getOne(q0).getPriceId();
        OzonePrice ozonePrice = this.priceService.getById(priceId);
        return ResponseFormat.success(ozonePrice);
    }

    @GetMapping("/coupon-code-item")
    @ApiOperation("获取当前优惠券码的详细信息 (是否可用 和 折扣率) 不报错code为0代表可用")
    public ResponseFormat codeItem(@RequestParam("code") String code) {
        if (code.length() != 14) {
            return ResponseFormat.fail("优惠码格式错误");
        }
        String letterCode = code.substring(0, 6);
        QueryWrapper<CouponCode> q1 = new QueryWrapper<>();
        q1.select("percentage", "expires_time", "count", "type");
        q1.eq("letter_pre", letterCode);
        CouponCode couponCode = this.couponCodeService.getOne(q1);
        if (couponCode == null) {
            return ResponseFormat.fail("当前券码不存在");
        }
        if (couponCode.getExpiresTime().getTime() < System.currentTimeMillis()) {
            return ResponseFormat.fail("当前券码已失效");
        }
        // 通用优惠码检查,所以这里为null
        if (this.couponCodeService.validCouponCode(code, null) == null) {
            return ResponseFormat.fail("当前券码不可用");
        }
        return ResponseFormat.success(couponCode);
    }

    /**
     * 支付-启动机器
     */
    @ApiOperation(value = "单次支付,返回wxPay所需信息,支付成功会自动启动机器")
    @PostMapping("/single-pay")
    @ForbidMultipleClick(key = "#singlePayBo.deviceId", msg = "机器冷却中", durationSecondsTime = 8)
    public ResponseFormat singlePay(@RequestBody @Validated SinglePayBo singlePayBo) {

        if (singlePayBo.getCouponId() != null && singlePayBo.getCouponCode() != null) {
            return ResponseFormat.fail("优惠券码和优惠券不可同时使用");
        }

        QueryWrapper<OzoneDevice> q1 = new QueryWrapper<>();
        q1.select("status", "price_id", "work_stop_time", "device_code");
        q1.eq("id", singlePayBo.getDeviceId());
        OzoneDevice ozoneDevice = this.ozoneDeviceService.getOne(q1);
        if (ozoneDevice == null) {
            return ResponseFormat.fail("请选择正确的机器");
        }
        if (ozoneDevice.getWorkStopTime().getTime() > System.currentTimeMillis()) {
            return ResponseFormat.fail("此机器正在使用中,结束时间:" + ozoneDevice.getWorkStopTime());
        }
        //停运/故障
        if (StringUtils.equals(ozoneDevice.getStatus(), DeviceStatus.STOP.getStatus()) || StringUtils.equals(ozoneDevice.getStatus(), DeviceStatus.FAULT.getStatus())) {
            return ResponseFormat.fail("此机器暂不提供服务");
        }

        // TODO 心跳检测已经关了
//        boolean onLine = mqttService.checkOnePayDevice(ozoneDevice.getDeviceCode());
//        if (!onLine){
//            return ResponseFormat.fail("当前机器未连接网络,停止服务");
//        }

        CouponUserIdAndPrice obj = this.getPriceAndCouponUserIdHaveCoupon(ozoneDevice.getPriceId(), singlePayBo);
        //支付信息-优惠券
        WxPayment wxPayment = this.payService.onePay(obj.price, MySecurity.getUserId(), singlePayBo.getDeviceId());
        String orderId = PayServiceImpl.getOrderId();
        // 去除延时队列,都去支付回调里解决吧
        if (obj.couponUserId != null) {
//            说明使用了优惠券,则要放入延时队列
//            delayTaskCommonService.couponAddCheckStatus(obj.couponId,obj.couponUserId,orderId);
            // 升级,放入redis重
            this.redisUtils.saveCoupon(orderId, obj.couponId);
        }
        if (obj.getCouponCode() != null) {
//             说明用了优惠码
//            delayTaskCommonService.couponCodeAddCheckStatus(singlePayBo.getCouponCode(),orderId);
            this.redisUtils.saveCouponCode(orderId, obj.couponCode);
        }
        //存入启动时间 可支付时间3分钟,所以redis有效期5钟
        this.redisUtils.saveOnePatFunctionTime(orderId, obj.seconds);

        return ResponseFormat.success(wxPayment);
    }

    private CouponUserIdAndPrice getPriceAndCouponUserIdHaveCoupon(Integer priceId, SinglePayBo singlePayBo) {
        OzonePrice ozonePrice = this.priceService.getById(priceId);
        BigDecimal price;
        Integer time;
        Integer priceLevel = singlePayBo.getPriceLevel();
        if (priceLevel == 1) {
            price = ozonePrice.getOnePrice();
            time = ozonePrice.getOneTime();
        } else if (priceLevel == 2) {
            price = ozonePrice.getTwoPrice();
            time = ozonePrice.getTwoTime();
        } else {
            price = ozonePrice.getThreePrice();
            time = ozonePrice.getTwoTime();
        }
        if (singlePayBo.getCouponId() == null && singlePayBo.getCouponCode() == null) {
            return new CouponUserIdAndPrice(price, time);
        } else if (singlePayBo.getCouponId() != null) {
            //拥有优惠券,记得将优惠券锁定3分钟
            //是否拥有此优惠券
            QueryWrapper<CouponUser> q1 = new QueryWrapper<>();
            q1.select("id", "expires_time", "status");
            q1.eq("coupon_id", singlePayBo.getCouponId());
            q1.eq("user_id", MySecurity.getUserId());
            CouponUser couponUser = this.couponUserService.getOne(q1);
            if (couponUser == null) {
                return new CouponUserIdAndPrice(price, time);
            }
            //优惠券不可用,直接原价
            if (!couponUser.getStatus().equals(CouponStatus.CAN_USE.getStatus()) || couponUser.getExpiresTime().getTime() < System.currentTimeMillis()) {
                return new CouponUserIdAndPrice(price, time);
            }
            //一层优惠券可用,再查询是否停用和定点业务
            QueryWrapper<Coupon> q2 = new QueryWrapper<>();
            q2.select("percentage", "stop_use");
            q2.eq("id", singlePayBo.getCouponId());
            q2.eq("type", Common.CouponType.onePayCoupon);
            Coupon coupon = this.couponService.getOne(q2);
            if (coupon == null) {
                // 当前优惠券非定点业务的
                return new CouponUserIdAndPrice(price, time);
            }
            if (coupon.getStopUse()) {
                //当前优惠券已经停用
                return new CouponUserIdAndPrice(price, time);
            }
            //可用
            BigDecimal percentage = new BigDecimal(String.valueOf(((double) coupon.getPercentage() / 100d)));
            //切记一定使用double,然后将double转string,再做运算
            price = new BigDecimal(String.valueOf(price.doubleValue()));
            couponUser.setStatus(CouponStatus.LOCK.getStatus());
            // 去除锁定状态,回调直接变成已使用
            // couponUserService.updateById(couponUser);
            return new CouponUserIdAndPrice(price.multiply(percentage), time, singlePayBo.getCouponId(), couponUser.getId());
        } else {
            // 优惠券码是否有效
            CouponCode couponCode = this.couponCodeService.validCouponCode(singlePayBo.getCouponCode(), Common.CouponType.onePayCoupon);
            if (couponCode == null) {
                return new CouponUserIdAndPrice(price, time);
            }
            Integer couponPercentage = couponCode.getPercentage();
            BigDecimal percentage = new BigDecimal(String.valueOf(((double) couponPercentage / 100d)));
            price = new BigDecimal(String.valueOf(price.doubleValue()));
            price = price.multiply(percentage);
            CouponUserIdAndPrice couponUserIdAndPrice = new CouponUserIdAndPrice(price, time);
            couponUserIdAndPrice.setCouponCode(singlePayBo.getCouponCode());
            return couponUserIdAndPrice;
        }
    }

    private class CouponUserIdAndPrice {
        private BigDecimal price;
        private Integer couponId;
        private Integer couponUserId;
        private Integer seconds;
        private String couponCode;

        public CouponUserIdAndPrice(BigDecimal price, Integer seconds, Integer couponId, Integer couponUserId) {
            this.price = price;
            this.seconds = seconds;
            this.couponId = couponId;
            this.couponUserId = couponUserId;
        }

        public CouponUserIdAndPrice(BigDecimal price, Integer seconds) {
            this.price = price;
            this.seconds = seconds;
        }

        public String getCouponCode() {
            return this.couponCode;
        }

        public void setCouponCode(String couponCode) {
            this.couponCode = couponCode;
        }
    }

}

